home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 November: Tool Chest / Dev.CD Nov 94.toast / Sample Code / Macintosh Sample Code / SC.014.CPlusTESample / Application.cp < prev    next >
Encoding:
Text File  |  1990-04-30  |  13.1 KB  |  585 lines  |  [TEXT/MPS ]

  1. /*------------------------------------------------------------------------------------------
  2.  
  3.     Program:    CPlusTESample 2.0
  4.     File:        Application.cp
  5.     Uses:       Application.h
  6.                 Document.h
  7.  
  8.     by Andrew Shebanow
  9.     of Apple Macintosh Developer Technical Support
  10.  
  11.     Copyright © 1989-1990 Apple Computer, Inc.
  12.     All rights reserved.
  13.  
  14. ------------------------------------------------------------------------------------------*/
  15.  
  16. // Mac Includes
  17. #ifndef __TYPES__
  18. #include <Types.h>
  19. #endif
  20. #ifndef __QUICKDRAW__
  21. #include <QuickDraw.h>
  22. #endif
  23. #ifndef __FONTS__
  24. #include <Fonts.h>
  25. #endif
  26. #ifndef __EVENTS__
  27. #include <Events.h>
  28. #endif
  29. #ifndef __CONTROLS__
  30. #include <Controls.h>
  31. #endif
  32. #ifndef __WINDOWS__
  33. #include <Windows.h>
  34. #endif
  35. #ifndef __RESOURCES__
  36. #include <Resources.h>
  37. #endif
  38. #ifndef __MENUS__
  39. #include <Menus.h>
  40. #endif
  41. #ifndef __TEXTEDIT__
  42. #include <TextEdit.h>
  43. #endif
  44. #ifndef __DIALOGS__
  45. #include <Dialogs.h>
  46. #endif
  47. #ifndef __DESK__
  48. #include <Desk.h>
  49. #endif
  50. #ifndef __SCRAP__
  51. #include <Scrap.h>
  52. #endif
  53. #ifndef __TOOLUTILS__
  54. #include <ToolUtils.h>
  55. #endif
  56. #ifndef __MEMORY__
  57. #include <Memory.h>
  58. #endif
  59. #ifndef __SEGLOAD__
  60. #include <SegLoad.h>
  61. #endif
  62. #ifndef __FILES__
  63. #include <Files.h>
  64. #endif
  65. #ifndef __OSUTILS__
  66. #include <OSUtils.h>
  67. #endif
  68. #ifndef __TRAPS__
  69. #include <Traps.h>
  70. #endif
  71.  
  72. #ifndef __APPLICATION__
  73. #include "Application.h"
  74. #endif
  75.  
  76. #ifndef __DOCUMENT__
  77. #include "Document.h"
  78. #endif
  79.  
  80. // OSEvent is the event number of the suspend/resume and mouse-moved events sent
  81. // by MultiFinder. Once we determine that an event is an osEvent, we look at the
  82. // high byte of the message sent to determine which kind it is. To differentiate
  83. // suspend and resume events we check the resumeMask bit.
  84. const short kOsEvent = app4Evt;                // event used by MultiFinder
  85. const short kSuspendResumeMessage = 0x01;    // high byte of suspend/resume event message
  86. const short kClipConvertMask = 0x02;        // bit of message field clip conversion
  87. const short kResumeMask = 0x01;                // bit of message field for resume vs. suspend
  88. const short kMouseMovedMessage = 0xFA;        // high byte of mouse-moved event message
  89.  
  90. extern "C" {
  91.     // from MPW standard library
  92.     void _DataInit();                // sets up A5 globals
  93. };
  94.  
  95. // useful state checking Boolean
  96. Boolean gHaveColorQD;
  97.  
  98. // just as "normal" global variables that are declared extern in
  99. // header files must still be declared somewhere in order to reserve
  100. // space, static class variables must also be declared outside of
  101. // the class definition. The syntax is confusing, but then, thats
  102. // what makes C++ so ***interesting***.
  103. OSType TApplication::fCreator;
  104.  
  105. TApplication::TApplication(OSType creator)
  106. {
  107.     SysEnvRec envRec;
  108.     long stkNeeded;
  109.     long heapSize;
  110.  
  111.     // initialize Mac Toolbox components
  112.     InitGraf((Ptr) &qd.thePort);
  113.     InitFonts();
  114.     InitWindows();
  115.     InitMenus();
  116.     TEInit();
  117.     InitDialogs((ResumeProcPtr) nil);
  118.     InitCursor();
  119.  
  120.     // Unload data segment: note that _DataInit must not be in Main!
  121.     UnloadSeg((ProcPtr) _DataInit);
  122.  
  123.     // ignore the error returned from SysEnvirons; even if an error occurred,
  124.     // the SysEnvirons glue will fill in the SysEnvRec
  125.     (void) SysEnvirons(curSysEnvVers, &envRec);
  126.  
  127.     // Are we running on a 128K ROM machine or better???
  128.     if (envRec.machineType < 0)
  129.       BigBadError(kErrStrings,eWrongMachine);        // if not, alert & quit
  130.  
  131.     gHaveColorQD = envRec.hasColorQD;
  132.  
  133.     // if we need more stack space, get it now
  134.     stkNeeded = StackNeeded();
  135.     if (stkNeeded > StackSpace())
  136.       {
  137.         // new address is heap size + current stack - needed stack
  138.         SetApplLimit((Ptr) ((long) GetApplLimit() - stkNeeded + StackSpace()));
  139.       }
  140.  
  141.     // Check for minimum heap size
  142.     heapSize = (long) GetApplLimit() - (long) ApplicZone();
  143.     if (heapSize < HeapNeeded())
  144.       BigBadError(kErrStrings,eSmallSize);
  145.  
  146.     // expand the heap so new code segments load at the top
  147.     MaxApplZone();
  148.  
  149.     // allocate an empty document list
  150.     fDocList = new TDocumentList;
  151.  
  152.     // check to see if WaitNextEvent is implemented
  153.     fHaveWaitNextEvent = TrapAvailable(_WaitNextEvent, ToolTrap);
  154.  
  155.     // initialize our class variables
  156.     fCurDoc = nil;
  157.     fDone = false;
  158.     fInBackground = false;
  159.     fMouseRgn = nil;
  160.     fWhichWindow = nil;
  161.     fCreator = creator;
  162. }
  163.  
  164. void TApplication::EventLoop()
  165. {
  166.     int gotEvent;
  167.     EventRecord tEvt;
  168.  
  169.     SetUp();                    // call setup routine
  170.     DoIdle();                    // do idle once
  171.  
  172.     while (!fDone)
  173.       {
  174.         // always set up fWhichWindow before doing anything
  175.         fWhichWindow = FrontWindow();
  176.         if (fWhichWindow != nil)
  177.           {
  178.             // see if window belongs to a document
  179.             fCurDoc = fDocList->FindDoc(fWhichWindow);
  180.             // make sure we always draw into correct window
  181.             SetPort(fWhichWindow);
  182.           }
  183.         else fCurDoc = nil;
  184.  
  185.         DoIdle();            // call idle time handler
  186.  
  187.         if (fHaveWaitNextEvent)
  188.           gotEvent = WaitNextEvent(everyEvent, &tEvt, SleepVal(), fMouseRgn);
  189.         else
  190.           {
  191.             SystemTask();
  192.             gotEvent = GetNextEvent(everyEvent, &tEvt);
  193.           }
  194.         fTheEvent = tEvt;
  195.  
  196.         // if we got a real event, process it
  197.         if (gotEvent)
  198.           ProcessEvent();
  199.  
  200.         AdjustCursor();
  201.       }
  202. }
  203.  
  204. void TApplication::ProcessEvent()
  205. {
  206.     // make sure alert is loaded in memory BEFORE we do any event
  207.     // processing. This is necessary since the alert for the case
  208.     // when we need to display an out of memory alert.
  209.     CouldAlert(rUserAlert);
  210.     TRY
  211.       {
  212.         AdjustCursor();
  213.         switch (fTheEvent.what)
  214.           {
  215.             case mouseDown:
  216.                 DoMouseDown();
  217.                 break;
  218.             case mouseUp:
  219.                 DoMouseUp();
  220.                 break;
  221.             case keyDown:
  222.             case autoKey:
  223.                 DoKeyDown();
  224.                 break;
  225.             case updateEvt:
  226.                 DoUpdateEvt();
  227.                 break;
  228.             case diskEvt:
  229.                 DoDiskEvt();
  230.                 break;
  231.             case activateEvt:
  232.                 DoActivateEvt();
  233.                 break;
  234.             case kOsEvent:
  235.                 DoOSEvent();
  236.                 break;
  237.             default:
  238.                 break;
  239.           }
  240.       }
  241.     RECOVER
  242.       {
  243.         AlertUser((short) gFailMessage, gFailError);
  244.         // don't let error bubble up any farther
  245.         goto doneEvent;
  246.       }
  247.     ENDTRY
  248.  
  249. doneEvent:
  250.     return;
  251. }
  252.  
  253. void TApplication::DoKeyDown()
  254. {
  255.     char key;
  256.     long mResult;
  257.  
  258.     key = (char) (fTheEvent.message & charCodeMask);
  259.     if ((fTheEvent.modifiers & cmdKey) && (fTheEvent.what == keyDown))
  260.       {
  261.         // only do command keys if we are not autokeying
  262.         AdjustMenus();                    // make sure menus are up to date
  263.         mResult = MenuKey(key);
  264.         if (mResult != 0)                // if it wasn't a menu key, pass it through
  265.           {
  266.             DoMenuCommand(HiWord(mResult), LoWord(mResult));
  267.             return;
  268.           }
  269.       }
  270.     if (fCurDoc != nil)
  271.       {
  272.         EventRecord tEvt;
  273.  
  274.         // we copy event record so that we don't pass reference to object field
  275.         tEvt = fTheEvent;
  276.         fCurDoc->DoKeyDown(&tEvt);
  277.       }
  278. }
  279.  
  280. void TApplication::DoActivateEvt()
  281. {
  282.     // event record contains window ptr
  283.     fWhichWindow = (WindowPtr) fTheEvent.message;
  284.     // see if window belongs to a document
  285.     fCurDoc = fDocList->FindDoc(fWhichWindow);
  286.     SetPort(fWhichWindow);
  287.  
  288.     if (fCurDoc != nil)
  289.       fCurDoc->DoActivate((fTheEvent.modifiers & activeFlag) != 0);
  290. }
  291.  
  292. void TApplication::DoUpdateEvt()
  293. {
  294.     // event record contains window ptr
  295.     fWhichWindow = (WindowPtr) fTheEvent.message;
  296.     // see if window belongs to a document
  297.     fCurDoc = fDocList->FindDoc(fWhichWindow);
  298.     SetPort(fWhichWindow);
  299.  
  300.     if (fCurDoc != nil)
  301.       fCurDoc->DoUpdate();
  302. }
  303.  
  304. // NOTE: we use an anonymous parameter here so that the compiler
  305. // doesn't warn us about it being unused. Since we give it a name
  306. // in the class definition, we still know what its used for.
  307. void TApplication::DoSuspend(Boolean)
  308. {
  309.     if (fCurDoc != nil)
  310.       fCurDoc->DoActivate(!fInBackground);
  311. }
  312.  
  313. void TApplication::DoResume(Boolean)
  314. {
  315.     if (fCurDoc != nil)
  316.       fCurDoc->DoActivate(!fInBackground);
  317. }
  318.  
  319. void TApplication::DoOSEvent()
  320. {
  321.     Boolean doConvert;
  322.     unsigned char evType;
  323.  
  324.     // is it a multifinder event?
  325.     evType = (unsigned char) (fTheEvent.message >> 24) & 0x00ff;
  326.     switch (evType) {     // high byte of message is type of event
  327.         case kMouseMovedMessage:
  328.             DoIdle();                    // mouse-moved is also an idle event
  329.             break;
  330.         case kSuspendResumeMessage:
  331.             doConvert = (fTheEvent.message & kClipConvertMask) != 0;
  332.             fInBackground = (fTheEvent.message & kResumeMask) == 0;
  333.             if (fInBackground)
  334.               DoSuspend(doConvert);
  335.             else DoResume(doConvert);
  336.             break;
  337.     }
  338. }
  339.  
  340. void TApplication::DoMouseDown()
  341. {
  342.     long mResult;
  343.     short partCode;
  344.     WindowPtr tWind;
  345.     EventRecord tEvt;
  346.  
  347.     // gotta watch those object field dereferences
  348.     partCode = FindWindow(fTheEvent.where, &tWind);
  349.     fWhichWindow = tWind;
  350.     tEvt = fTheEvent;
  351.     switch (partCode)
  352.       {
  353.         case inSysWindow:
  354.             DoMouseInSysWindow();
  355.             break;
  356.         case inMenuBar:
  357.             AdjustMenus();
  358.             mResult = MenuSelect(tEvt.where);
  359.             if (mResult != 0)
  360.               DoMenuCommand(HiWord(mResult),LoWord(mResult));
  361.             break;
  362.         case inGoAway:
  363.             DoGoAway();
  364.             break;
  365.         case inDrag:
  366.             DoDrag();
  367.             break;
  368.         case inGrow:
  369.             if (fCurDoc != nil)
  370.               fCurDoc->DoGrow(&tEvt);
  371.             break;
  372.         case inZoomIn:
  373.         case inZoomOut:
  374.             if ((TrackBox(fWhichWindow, tEvt.where, partCode)) &&
  375.                 (fCurDoc != nil))
  376.               fCurDoc->DoZoom(partCode);
  377.             break;
  378.         case inContent:
  379.             // If window is not in front, make it so
  380.             if ( fWhichWindow != FrontWindow() )
  381.               SelectWindow(fWhichWindow);
  382.             else if (fCurDoc != nil)
  383.               fCurDoc->DoContent(&tEvt);
  384.             break;
  385.       }
  386. }
  387.  
  388. void TApplication::DoDrag()
  389. {
  390.     DragWindow(fWhichWindow, fTheEvent.where, &qd.screenBits.bounds);
  391. }
  392.  
  393. void TApplication::DoGoAway()
  394. {
  395.     if (TrackGoAway(fWhichWindow, fTheEvent.where))
  396.       {
  397.         if (fCurDoc != nil)
  398.           {
  399.             if (fCurDoc->DoClose(true, yesResult, false) != cancelResult)
  400.               {
  401.                 fDocList->RemoveDoc(fCurDoc);
  402.                 delete fCurDoc;
  403.               }
  404.           }
  405.         else CloseDeskAcc(((WindowPeek) fWhichWindow)->windowKind);
  406.         // make sure our current document/window references are valid
  407.         fWhichWindow = FrontWindow();
  408.         if (fWhichWindow != nil)
  409.           {
  410.             fCurDoc = fDocList->FindDoc(fWhichWindow);
  411.             SetPort(fWhichWindow);
  412.           }
  413.         else fCurDoc = nil;
  414.       }
  415. }
  416.  
  417. void TApplication::ProcessArgs()
  418. {
  419.     short message, numFiles, curFile;
  420.     AppFile fileInfo;
  421.  
  422.     /* count the files */
  423.     CountAppFiles(&message,&numFiles);
  424.     if (numFiles == 0)
  425.       {
  426.         // create a single empty document
  427.         DoNew();
  428.         return;
  429.       }
  430.     for (curFile = 1; curFile <= numFiles; curFile++)
  431.       {
  432.         /* get file info */
  433.         GetAppFiles(curFile,&fileInfo);
  434.         /* open/print the file */
  435.         if (message != appPrint)
  436.           {
  437.             TRY
  438.               {
  439.                 OpenADoc(fileInfo.vRefNum,0,fileInfo.fName,fileInfo.fType);
  440.               }
  441.             RECOVER
  442.               {
  443.                 goto processNextFile;
  444.               }
  445.             ENDTRY
  446.           }
  447. processNextFile:
  448.         /* clear finder arg for this file */
  449.         ClrAppFiles(curFile);
  450.       }
  451. }
  452.  
  453. void TApplication::DoQuit(Boolean askUser, YNCResult defaultResult)
  454. {
  455.     while (true)
  456.       {
  457.         fWhichWindow = FrontWindow();
  458.         if (fWhichWindow == nil)
  459.           break;
  460.         fCurDoc = fDocList->FindDoc(fWhichWindow);
  461.         if (fCurDoc != nil)
  462.           {
  463.             // if the user cancels the quit
  464.             if (fCurDoc->DoClose(askUser, defaultResult, true) == cancelResult)
  465.               return;
  466.             else
  467.               {
  468.                 fDocList->RemoveDoc(fCurDoc);
  469.                 delete fCurDoc;
  470.               }
  471.           }
  472.         else CloseDeskAcc(((WindowPeek) fWhichWindow)->windowKind);
  473.         // make sure we aren't in an infinite loop. This could occur
  474.         // if the CloseDeskAcc was failing for some reason.
  475.         if (FrontWindow() == fWhichWindow)
  476.           {
  477.             // send the window to the back of the list.
  478.             // if the FrontWindow is still the same, we will exit
  479.             // the loop. Otherwise, we let the loop keep running so
  480.             // that we have a chance to close our other windows
  481.             // cleanly
  482.             SendBehind(fWhichWindow, nil);
  483.             if (FrontWindow() == fWhichWindow)
  484.               break;
  485.           }
  486.       }
  487.     fDone = true;
  488.     fWhichWindow = nil;
  489.     fCurDoc = nil;
  490. }
  491.  
  492. Boolean TApplication::TrapAvailable(short tNumber,TrapType tType)
  493. {
  494.     // Check and see if the trap exists. On 64K ROM machines, tType will be ignored.
  495.     return NGetTrapAddress(tNumber, tType) != GetTrapAddress(_Unimplemented);
  496. }
  497.  
  498. void TApplication::WDToDirID(short wdRefNum, short& vRefNum, long& dirID)
  499. {
  500.     const short kRootDirID = 2;
  501.     long junk;
  502.  
  503.     OSErr err = GetWDInfo(wdRefNum,&vRefNum,&dirID,&junk);
  504.     if (err != noErr)
  505.       {
  506.         vRefNum = wdRefNum;        // if GetVol doesn't return valid vRefNum/dirID pair,
  507.         dirID = kRootDirID;        // use wdRefNum as a vRefNum and use root for dirID
  508.       }
  509. }
  510.  
  511. void AlertUser(short errResID, short errCode)
  512. {
  513.     Str255 messageStr;
  514.  
  515.     // if we have a hilited menu, turn it off before displaying alert
  516.     HiliteMenu(0);
  517.  
  518.     if (errResID != 0)
  519.       {
  520.         GetIndString(messageStr, errResID, errCode);
  521.         ParamText(messageStr, "\p", "\p", "\p");
  522.       }
  523.     else
  524.       {
  525.         // we need to lookup the error in our table
  526.         LookupErrorString(errCode,kSysErrStrings,messageStr);
  527.         ParamText(messageStr, "\p", "\p", "\p");
  528.       }
  529.     SetCursor(&qd.arrow);
  530.     (void) Alert(rUserAlert, (ModalFilterProcPtr) nil);
  531. }
  532.  
  533. void BigBadError(short errResID, short errCode)
  534. {
  535.     AlertUser(errResID,errCode);
  536.     ExitToShell();
  537. }
  538.  
  539. Boolean LookupErrorString(short value, short resID, StringPtr str)
  540. {
  541.     struct ErrRecord {
  542.         short lowErr;
  543.         short highErr;
  544.         short index;
  545.     };
  546.     typedef struct ErrRecord* ErrRecordPtr;
  547.  
  548.     Handle            table;
  549.     ErrRecordPtr    pEntry;
  550.     unsigned long    tableOffset;
  551.     long            lenTab;
  552.     int                strID;
  553.  
  554.     // start with an empty string
  555.     str[0] = 0;
  556.  
  557.     table = GetResource('errs', resID);
  558.     if (!table)
  559.       {
  560.         lenTab = (long) (GetHandleSize((Handle) table) / sizeof(ErrRecord));
  561.  
  562.         strID = 0;
  563.         tableOffset = 0;
  564.  
  565.         for (long i = 1; i <= lenTab; i++)
  566.           {
  567.             pEntry = (ErrRecordPtr) ((unsigned long) *table) + tableOffset;
  568.  
  569.             if (pEntry->lowErr == 0)
  570.               strID = pEntry->index;
  571.             else if ((pEntry->lowErr <= value) && (value <= pEntry->highErr))
  572.               {
  573.                 if (pEntry->index > 0)
  574.                   GetIndString(str, strID, pEntry->index);
  575.                 return true;
  576.               }
  577.  
  578.             tableOffset += sizeof(ErrRecord);
  579.           }
  580.       }
  581.     return false;
  582. }
  583.  
  584. // That's all, folks...
  585.